package com.example.lock.CoditionAQS;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 基于Object实现等待/通知机制的相关方法
 * Created by ZMJ on 2019/12/30.

 执行结果:
 Thread[WaitThread,5,main] flag is true. await @ 00:05:55
 Thread[notifyThread,5,main] hold lock. notify @ 00:05:55
 Thread[notifyThread,5,main] hold lock again. sleep @ 00:06:00
 Thread[WaitThread,5,main] flag is false. running @ 00:06:05

 这里有几个需要注意的点:
 结果第三行和第四行的顺序有可能颠倒，因为是竞争获取锁的
 wait()方法被执行后，锁被自动释放，但notify()方法被执行后，锁却不自动释放
 必须执行完notify()方法所在的同步synchronized代码块后才释放锁

 */
public class WaitNotify {

    // 代码来自《Java并发编程的艺术》
    static boolean flag = true;
    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        TimeUnit.SECONDS.sleep(1);
        Thread notifyThread = new Thread(new Notify(), "notifyThread");
        notifyThread.start();
    }

    static class Wait implements Runnable {

        @Override
        public void run() {
            synchronized (lock) {
                // 条件不满足时，继续wait，同时释放了lock的锁
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. await @ "
                                + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 条件满足，完成工作
                System.out.println(Thread.currentThread() + " flag is false. running @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            }
        }
    }

    static class Notify implements Runnable {

        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock. notify @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
                // 暂停5秒
                SleepUtils.second(5);
            }
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " hold lock again. sleep @ "
                        + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                SleepUtils.second(5);
            }
        }
    }

    static class SleepUtils {
        public static void second(int n) {
            try {
                TimeUnit.SECONDS.sleep(n);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}
